
// 需要给外部库暴漏查询版本的接口用于升级
// console.log(window.location.href)
// "csp": "default-src 'self' blob: data: filesystem: ws: http: https: 'unsafe-eval' 'unsafe-inline'",


const callbackMap = new Map()
// 注册事件回调
async function addEventCallback(eventType, callback) {
    if (callbackMap.has(eventType)) {
        return new Error(`不能重复注册 ${eventType} 事件回调`);
    }
    if (callback) {

        let unlistenFn
        if ('close' === eventType) {
            //await closeCameraWindow()
            unlistenFn = await __onCloseWindow(callback)
        } else {
            const { listen } = window.__TAURI__.event
            unlistenFn = await listen(eventType, (event) => {
                (typeof callback === "function") && callback(event.payload)
                //判断 eventType
                //if ('startRecording' == eventType) {
                // 可以在回调时 给ws 发送消息使图标闪烁
                //__sendWsMessage("startRecording")
                //}
            })
        }
        callbackMap.set(eventType, unlistenFn)
    }
}

function removeEventCallback(eventType) {
    let unlistenFn = callbackMap.get(eventType);
    if (unlistenFn) {
        (typeof callback === "function") && unlistenFn()
    }
}

function __removeAllEventCallback() {
    callbackMap.forEach(function (unlistenFn, _key) {
        (typeof callback === "function") && unlistenFn()
    })
}



async function __closeWindow() {
    const { getCurrentWindow } = window.__TAURI__.window
    getCurrentWindow().close()
}

async function __onCloseWindow(callbackFn) {
    const { getCurrentWindow } = window.__TAURI__.window
    const { confirm } = window.__TAURI__.dialog
    const unlisten = await getCurrentWindow().onCloseRequested(async (event) => {
        const confirmed = await confirm('确定要关闭窗口?', '系统提示')
        const r = await callbackFn(confirmed)
        if (!r) {
            event.preventDefault()
        }
        else {
            // closeCameraWindow() // 关闭摄像头窗口
        }
    });
    return unlisten
}

async function __invoke(method, args = {}) {
    const { invoke } = window.__TAURI__.core
    const r = await invoke(method, args)
    //console.log(`invoke method=${method},args=${JSON.stringify(args)},result=${JSON.stringify(r)}`)
    return r
}


//开始录屏
async function startRecording(outputFile = "./output.mkv", watermarkText = '水印文字', forceOpenCamera = false /*强制摄像头必须开启*/) {
    return __invoke("start_recording", { outputFile, watermarkText, forceOpenCamera })
}

async function validateChecksum(outputFile) {
    return __invoke("validate_checksum", { outputFile })
}
/// 不传key 为空时返回全部状态信息,否则返回对应key 的信息
/// 传入 key='isRecording' 返回 1 代表正在录屏,0 表示没有录屏 
/// 传入 上传和下载文件的key 返回其状态 0-100 正常, >100 为错误
async function getStatus(key) {
    return __invoke("get_status", { key })
}



//停止录屏
// 如果没有错误的情况下会返回 checksum 字符串
// 但是返回的 checksum 可能为null,这在多次调用这个函数时产生
async function stopRecording() {
    return __invoke("stop_recording")
}



// 全屏
async function setFullscreen(enable = true) {
    const { getCurrentWindow } = window.__TAURI__.window
    const win = await getCurrentWindow();
    await win.setFullscreen(enable)
    await win.setAlwaysOnTop(enable)
    await win.setFocus()
    
}
async function maximizeWindow(enable = true) {
    const { getCurrentWindow } = window.__TAURI__.window
    await getCurrentWindow().maximize();
}

async function selectFile({ path, multiple = false, suffix = ['mkv'] }) {

    let defaultPath = path
    if (!defaultPath) {
        const { desktopDir } = window.__TAURI__.path
        defaultPath = await desktopDir()
    }

    const { open } = window.__TAURI__.dialog
    const selected = await open({
        multiple,
        directory: false,
        defaultPath,
        filters: [{
            name: '文件',
            extensions: [...suffix]
        }]
    });

    if (selected !== null) {
        return multiple ? selected : [selected]
    }

}

async function selectPath(path) {
    let defaultPath = path
    if (!defaultPath) {
        const { desktopDir } = window.__TAURI__.path
        defaultPath = await desktopDir()
    }

    const { open } = window.__TAURI__.dialog
    const selected = await open({
        directory: true,
        defaultPath
    })
    if (Array.isArray(selected)) {
        // user selected multiple directories
        selectPath = selected[0]
    } else if (selected === null && !defaultPath) {
        // 取消选择且传入的 defaultPath 
        return defaultPath
    } else {
        // user selected a single directory
        selectPath = selected
    }
    return selectPath
}


// 打开任意一个目录
// 注意在windows 上目录必须是反斜杠
// await openPath('d:\\')
async function openPath(path) {
    return await __invoke("open_path", { path })
}

async function getSysinfo(path) {
    return await __invoke("get_sysinfo", { path })
}

async function pathExists(path) {
    return await __invoke("path_exists", { path })
}

// 启用键盘
async function enableKeyboard(enable = true) {
    let r
    if (enable) {
        r = await __invoke("start_hook", { cmd: "stop" })
    } else {
        r = await __invoke("start_hook", { cmd: "start" })
    }
    return r
}

// 获取虚拟桌面个数
async function getDesktopCount() {
    return await __invoke("get_desktop_count")
}

/// 检查环境,包括虚拟机和远程桌面
async function checkEnv() {

    let ok = await __invoke("check_env")
    if (!ok) {
        const { message } = window.__TAURI__.dialog
        await message('本软件不支持在虚拟机或远程桌面中运行!', { title: '警告', type: 'error' });
        __closeWindow()
    }
    return true
}


async function checkProcess(targetNames = [], isKill = false) {
    if (targetNames.length > 0) {
        // 需要检查的进程名称以分号分割
        let command = isKill ? "kill_process" : "check_process"
        let r = await __invoke(command, { targetNames })
        console.log(r)

        let resultObj = {}
        r.forEach((kv) => {
            resultObj[kv[0]] = kv[1]
        });
        return resultObj
    }
}
/**
 * 
 * @param {*} args 结构如下 {
 * key: String,
 * srcFile: String,
 * desPath: String,
 * desFilename: String,
 * host: String,
 * port: u32,
 * username: String,
 * password: String
 *}
 * @returns 
 */
async function upload(args /*,progressHandler*/) {
    return await __invoke('upload', { args });

}
/**
 * 
 * @param {*} args =[ {key: String,saveName: String, savePath: String, url: String}]
 * @param {*} headers  是一个map 
 */
async function downloadPackage(args, /* progressHandler,*/ headers) {
    await __invoke('download_package', { args, headers: headers ?? {} })
}
/**
 * 解决在升级之后使用a标签无法下载文件
 * @param {*} url 
 * @param {*} saveName 
 * @returns 
 */
async function downloadFile(url,saveName,headers={}) {
  return await __invoke('download_file', { url, saveName,headers })
}


//打包文件
async function package(zipPath, filePath) {
    return await __invoke('package', { zipPath, filePath });
}

async function unpackage({ zipPath, destPath, password }) {
    return await __invoke('unpackage', { zipPath, destPath, password });
}
/// 获取桌面路径
async function getDesktop() {
    const { desktopDir } = window.__TAURI__.path
    return await desktopDir()
}
async function __log(msg, level = 'info') {
    if (typeof msg !== "string") { msg = JSON.stringify(msg) }
    __invoke('log', { msg, level })
}

async function logI(msg) {
    __log(msg)
}

async function logE(msg,) {
    __log(msg, level = 'error')
}

async function logAllProcess() {
    return await __invoke('log_all_process')
}

async function execCmd(cmd, args = []) {
    return await __invoke('exec_cmd', { cmd, args })
}


async function getExeDir() {
    return await __invoke('get_exe_dir')
}

async function __getMsgs() {
    return await __invoke('get_msg')
}
async function __ackMsg({ id }) {
    return await __invoke('ack_msg', { id })
}
let getMsgIntervalId = null
let ackSet = null
let getMsgTimes = 0
// 消息循环是否正在执行
let messageLoopIsRunning = false
async function startMessageLoop(callback = async (msg) => { console.log(msg); /* msg.ack() */ }, interval = 1000) {
    ackSet = new Set()
    if (!getMsgIntervalId) {
        getMsgIntervalId = setInterval(async () => {
            // 消息循环是否正在执行 直接返回
            if (messageLoopIsRunning) {
                return
            }
            messageLoopIsRunning = true

            try {
                if (getMsgTimes % 60 == 0) {//每过60次清除ackSet
                    ackSet.clear()
                    //console.log("ackSet 清除, 当前次数=%s", getMsgTimes)
                }
                getMsgTimes++;
                const messages = await __getMsgs()
                if (messages && messages.length > 0) {
                    for (const m of messages) {
                        // 安装ack 函数,需要在akc 的时候把ack的消息添加到set,在成功的时候删除,每次有新消息的时候需要过滤消息判断是否ack 过
                        m.ack = () => {
                            if (ackSet.has(m.id)) {
                                return
                            }
                            //异步ack 
                            __ackMsg(m).then((id) => {
                                //成功后删除
                                //ackSet.delete(m)
                                //console.log("===ack success msgid=%s", id)
                                // 添加到ackSet ,用于过滤,防止重复调用  callback
                                ackSet.add(id);
                            });


                        }
                        // 客户端需要在合适的时候调用ack
                        if (!ackSet.has(m.id)) {
                            callback(m)
                        }
                    }
                }
            } finally {
                messageLoopIsRunning = false
            }


        }, interval)
    } else {
        console.warn("消息循环已经启动,请不要重复调用,如果需要请先调用 stopMessageLoop()停止消息循环")
    }
}

function stopMessageLoop() {
    if (getMsgIntervalId) {
        clearInterval(getMsgIntervalId)
        getMsgIntervalId = null
        ackSet = null
    }
}

async function version() {
    return await __invoke('get_version')
}

// 暴漏给客户端的api
const APIX = {
    version,
    startRecording,
    stopRecording,
    setFullscreen,

    openPath,
    selectPath,
    pathExists,
    selectFile,

    enableKeyboard,
    getDesktopCount,
    checkProcess,
    checkEnv,
    getSysinfo,

    addEventCallback,
    downloadPackage,
    downloadFile,
    upload,
    getDesktop,
    unpackage,
    logI,
    logE,
    logAllProcess,
    validateChecksum,
    getStatus,
    execCmd,
    maximizeWindow,
    getExeDir,
    startMessageLoop,
    stopMessageLoop,
}


function __init_apix() {
    window.__TAURI__.APIX = APIX
    console.log("💓💓💓💓💓init api success !!!!!!!!")
    // 检查是否虚拟机
    //checkEnv()
}


window.onbeforeunload = function () {
    // console.log("========beforeunload")
    stopMessageLoop()
    __removeAllEventCallback()
}

//=============================以上内容不要修改==================================

// document.addEventListener('DOMContentLoaded', function () {
// //注意此js 在每次加载不同的url 时 都会重新执行
// console.log('init.js' + new Date())
// initWindowRightMenu()
// })


// 测试用 初始化窗口右侧菜单
function initWindowRightMenu() {

    let menu = document.createElement("div")
    menu.style = `
            background: wheat;
            position: fixed;
            z-index: 999;
            right: 0;
            top: 50%;
            transform: translate(0, -50%);`

    function creatMenuItem(html, clickFn) {
        let menuItem = document.createElement("div")
        menuItem.style = `
                cursor: pointer;
                margin:5px;
            `
        menuItem.innerHTML = html
        menuItem.onclick = clickFn
        menu.appendChild(menuItem)
    }

    creatMenuItem("进入全屏", async () => {
        // setTimeout(async () => { await setFullscreen(true) }, 10 * 1000)
        await setFullscreen(true)
    })

    creatMenuItem("退出全屏", async () => {
        await setFullscreen(false)
    })

    creatMenuItem("开始录屏<span id='recordTimeSpan' ><span>", async () => {

        // 开始录屏
        // startRecording("output", '薛')
        // -movflags +frag_keyframe+empty_moov	mkv实时可用	直播、录屏
        let r = await startRecording(`d:\\output1.mkv`, '薛1', false)
        stopMessageLoop();
        startMessageLoop(async (msg) => {
            if (msg.flag == 'RECORDING') {
                document.getElementById("recordTimeSpan").textContent = `${msg.status}:${msg.content}`
                setTimeout(() => {
                    msg.ack()
                }, 3000);
            }
            console.log(msg);
        });
        // 注册回调函数
        // addEventCallback('startRecording', (msg) => { document.getElementById("recordTimeSpan").textContent = msg })
    })

    creatMenuItem("停止录屏", async () => {
        let checksum = await stopRecording()
        console.log("checksum=%s", checksum)
        // alert(checksum)
    })

    creatMenuItem("打开目录", async () => {
        //await openSavePath()
        // 注意在windows 上目录必须是反斜杠
        await openPath('d:\\')
    })



    creatMenuItem("禁用按键", async () => {
        // 注意发送参数的格式，可能导致 服务端解析不了
        let r = await enableKeyboard(false)
        alert(r)
    })
    creatMenuItem("启用按键", async () => {
        let r = await enableKeyboard()
        alert(r)
    })


    creatMenuItem("桌面个数", async () => {
        let r = await getDesktopCount()
        alert(r)
    })

    creatMenuItem("杀死进程", async () => {
        // 需要杀死的进程名称以分号分割
        // 进程的名称带不带exe 得经过测试
        let r1 = await checkProcess(["Notepad.exe", "Notepad.exe"])
        console.log("checkReuslt: %o", r1)
        let r2 = await checkProcess(["Notepad.exe", "Notepad.exe"], true)
        console.log("killReuslt: %o", r2)

    })

    creatMenuItem("检查环境", async () => {
        let r = await checkEnv()
        alert(r)
    })


    creatMenuItem(`上传录屏:<span id="uploadProgress">0%</span>`, async () => {
        let args = {
            key: "0",
            // 上传文件本地全路径,可以用selectFile 函数获取
            srcFile: "E:/BaiduNetdiskDownload/01.高数基础/01新文道版本/31.第五章-定积分的应用.mp4",
            // 目的路径，参数需要在java端加密,路径必须以/开始
            desPath: "/个人文件/八维教育上地校区/游戏学院/",
            // 保存名称，参数需要在java端加密
            desFilename: "a.mp4",//，参数需要在java端加密
            // D:\soft\apache-ftpserver-1.2.0
            host: "SUhWTU1WTU1WTU0=x",// "10.55.55.55",//参数需要在java端加密
            port: 21,//参数需要在java端加密
            username: "AREWABEZFxQRGRY=x",// "yinxiaolian",//，参数需要在java端加密,或者用同一个用户
            password: "Tk5OTk5Ox",// "666666",//参数需要在java端加密
        }
        //  key 需要自定义
        //  status 状态,上传中 或完成 或 error
        //  percent 下载的百分比 ,如果是error 则 percent=0

        stopMessageLoop();
        startMessageLoop(async (msg) => {
            if (msg.flag == 'UPLOAD') {
                let body = `${msg.key}-${msg.status}-${msg.content}`
                document.getElementById("uploadProgress").innerText = body
            }
        });


        upload(args).catch((error) => {
            alert(error)
        })

        //添加事件回调
        // addEventCallback('upload', callback)

    })

    creatMenuItem(`下载文件:<span id="downloadProgress"></span>`, async () => {
        /* 下载单个文件
        download(
            'http://localhost:8081/a.mp4', //下载路径
            'd:/bbb.mp4'                //保存地址
            ,({fileIndex,status,percent}) => {
                // console.log(`Uploaded ${percent} `)
                document.getElementById("downloadProgress").innerText = `${percent}%`
            }
        ).catch((error) =>{
            alert(error)
        })
        */

        let args = {
            // 本地加密压缩包名称 ,废弃packageName
            // packageName:"考试1附件20241030",
            // 保存路径,可以用 selectPath 选择后传入
            // 要下载的文件列表
            files: [ // url 参数需要在java端加密
                // key 要保证唯一 ,并且简短
                // saveName 是保存文件的名称 ，savePath 保存路径
                //  { key: "1", saveName: "a.mp4", savePath: "d:/a/b", url: "http://localhost:8081/a.mp4" },
                //  { key: "2", saveName: "b.mp4", savePath: "d:/a/b", url: "http://localhost:8081/a.mp4" },
                { key: "cz01", saveName: "c.z01", savePath: "d:/a/b", url: "http://172.16.10.139:9800/app/file/exam_core_paper_client_split_file/1e89073063804152a99d9876b318618c/b8ffcba369384b6b85f2cc5746971075.z01" }
            ]
        }
        //  key 需要自定义
        //  status 状态,下载中,下载完成，打包完成 或 error
        //  percent 下载的百分比 ,如果是error 则 percent=0
        // let callback = ({ key, status, percent }) => {
        //     let msg = `${key}-${status}-${percent}`
        //     //  console.log(msg)
        //     document.getElementById("downloadProgress").innerText = msg
        //     msg.ack()
        // }




        stopMessageLoop();
        startMessageLoop(async (msg) => {
            if (msg.flag == 'DOWNLOAD') {
                const { status, content } = msg
                let info = `${status}-${content}`
                //  console.log(msg)
                document.getElementById("downloadProgress").innerText = info
                msg.ack()
            }
            console.log(msg);
        });
        downloadPackage(args)


        //添加事件回调
        // addEventCallback('downloadPackage', callback)

        // __removeAllEventCallback()

    })

    creatMenuItem("选择文件并验证", async () => {
        const files = await selectFile()
        console.log("file: %o", files)

        const file = files[0]
        const checksum = await validateChecksum(file)

        alert(`file:${file},checksum:${checksum}`)
    })

    creatMenuItem("测试跳转index", async () => {
        window.location.href = "index.html"
    })
    creatMenuItem("测试跳转index1", async () => {
        window.location.href = "index1.html"
    })

    creatMenuItem("系统信息", async () => {
        logI("这是日志")

        // 获取系统信息包括磁盘剩余容量
        let r = await getSysinfo("d:/aaa")

        logI(r)
        //检查路径或文件是否存在
        let r1 = await pathExists("d:\\aaa")

        logI(r1)

        let r2 = await getStatus()
        console.log(r2)

        try {
            let r3 = await execCmd("tasklist", ["/FO1", "CSV", "/NH"]).catch(e => console.error(e))
            let r4 = await execCmd("echo", ["%%cd%%"]).catch(e => console.error(e))

            //let r4 = await execCmd("taskkill", ["/F", "/IM", "notepad.exe", "/T"])

            //let r3 = await execCmd("echo", ["aaa", "|", "findstr","a"])
            //let r3 = await execCmd("echo", ["aaa哈哈aa"])
            //maximizeWindow()

            logI(r3);
            logI("pwd:%s", r4);
        } catch (e) {
            logE(e)
        }

        const dir = await getExeDir()
        logI("getExeDir=%s", dir)

        // const msg = await get_msg()
        // await ack_msg(msg[0].id)


    })


    creatMenuItem("测试解压", async () => {
        let args = {
            zipPath: "D:\\unzip\\testSpliteOut\\684f3fe8c59143878047cff242843f42.zip",
            // 解压到路径,可以用 selectPath 选择后传入
            destPath: "d:/unzip/testSpliteOut/to",
            // 要下载的文件列表
            password: "aba0d2c36a664270af59f92dac3f5aab"
        }
        let r = await unpackage(args)
        alert(r)
    })

    creatMenuItem("添加关闭窗口监听", async () => {

        addEventCallback('close', async (confirmed) => {
            let canClose = false
            if (confirmed) {// 如果点击了确认按钮
                canClose = true
            }
            // 返回true 表示关闭,false 表示阻止关闭
            return canClose
        })
    })

    creatMenuItem("测试文件路径选择", async () => {

        const r1 = await selectFile({ path: "D:\\a\\b" })
        const r2 = await selectPath("D:\\a")
    })

    creatMenuItem("前端下载文件", async () => {
       
        const url = "http://172.16.10.111:5886/app/file/exam_core_question/2025-04-25/edbd36af0f3d4b26880c6ec58d2d1bd2.zip"; // 测试大文件下载
        const saveName = "附件.zip";   
        try{
          const savePath= await downloadFile(url,saveName,{"aaa":"aavavalue"})
          alert(`文件已保存到: ${savePath}`);
        }catch(e){
          alert(e)
        }

        const content = "111222";
        const fileName = "a.txt";

        const blob = new Blob([content], { type: "text/plain" });
        const url1 = URL.createObjectURL(blob);

        const aTag = document.createElement("a");
        aTag.href = url1;
        aTag.download = fileName;
        aTag.click();

        // 释放URL资源
        //URL.revokeObjectURL(url1);
        
    })

    document.body.appendChild(menu)
}
__init_apix()
